﻿using System;
using System.IO;
using System.Net;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Net.Sockets;
using System.Threading;
using System.Text;

namespace MUD.Network
{
    /// <summary>
    /// This class is designed for use as the object to be assigned to the SocketAsyncEventArgs.UserToken property. 
    /// </summary>
    public class AsyncClient
    {
        private Socket _socket;
        private Guid _guid;
        public SocketAsyncEventArgs RecieveArgs;
        public SocketAsyncEventArgs SendArgs;
        private Queue<string> MessageBuffer = new Queue<string>();

        public event EventHandler OnDisconnectRequest;

        public bool HasBufferedMessages
        {
            get { return MessageBuffer.Count > 0; }
        }

        public event EventHandler OnSendSynchronous;

        public AsyncClient() 
        {
            _guid = Guid.NewGuid();
            RecieveArgs = new SocketAsyncEventArgs();
            SendArgs = new SocketAsyncEventArgs();
        }

        public AsyncClient(Socket socket)
        {
            _socket = socket;
            _guid = Guid.NewGuid();
            RecieveArgs = new SocketAsyncEventArgs();
            RecieveArgs.AcceptSocket = socket;
            SendArgs = new SocketAsyncEventArgs();
            SendArgs.AcceptSocket = socket;
        }

        public AsyncClient(Socket socket, int bufferLength)
        {
            _socket = socket;
            _guid = Guid.NewGuid();
            RecieveArgs = new SocketAsyncEventArgs();
            RecieveArgs.AcceptSocket = socket;
            RecieveArgs.SetBuffer(new byte[bufferLength], 0, bufferLength);
            SendArgs = new SocketAsyncEventArgs();
            SendArgs.AcceptSocket = socket;
        }

        public Socket Socket
        {
            get { return _socket; }
            set 
            {
                _socket = value;
                RecieveArgs.AcceptSocket = _socket;
                SendArgs.AcceptSocket = _socket;
            }
        }

        public Guid Guid
        {
            get 
            {
                if (_guid == Guid.Empty)
                    _guid = Guid.NewGuid();
                return _guid; 
            }
        }

        public IPAddress Address
        {
            get
            {
                return ((IPEndPoint)_socket.RemoteEndPoint).Address;
            }
        }

        public override string ToString()
        {
            return Guid.ToString() + "@" + Address.ToString();
        }

        public void Write(string message)
        {
            byte[] buffer = ASCIIEncoding.ASCII.GetBytes(message);
            SendArgs.SetBuffer(buffer, 0, buffer.Length);
            try
            {
                if (!Socket.SendAsync(SendArgs) && OnSendSynchronous != null)
                {
                    OnSendSynchronous(this, new EventArgs());
                }
            }
            catch (Exception ex)
            {
                MessageBuffer.Enqueue(message);
            }
        }

        public void SendQueued()
        {
            Write(MessageBuffer.Dequeue());
        }

        public void Disconnect()
        {
            if (OnDisconnectRequest != null)
                OnDisconnectRequest(this, new EventArgs());
        }

    }
}